#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }

    #[test]
    fn mongodb() {
        let db: mongodb::Database = unimplemented!();
        let where_ = serde_json::Map::new();
        let where_ = where_.into_iter();
        let r = db.collection("fsdfa").aggregate(where_, None).await;
    }
}

#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate log;

pub use async_trait::*;
// use mongodb::bson::Document as Wrapper;
pub use mongodb_orm_drive::*;
use std::fmt::{Debug, Formatter};

pub use futures;
pub use mongodb;

#[derive(Debug)]
pub enum Error {
    Wrapper(String),
    BsonSer(mongodb::bson::ser::Error),
    BsonDeser(mongodb::bson::de::Error),
    BsonExtjson(mongodb::bson::extjson::de::Error),
    Oid(mongodb::bson::oid::Error),
    Database(mongodb::error::Error),
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:?}", self)
    }
}
impl From<mongodb::error::Error> for Error {
    fn from(err: mongodb::error::Error) -> Self {
        Self::Database(err)
    }
}
impl From<mongodb::bson::ser::Error> for Error {
    fn from(err: mongodb::bson::ser::Error) -> Self {
        Self::BsonSer(err)
    }
}
impl From<mongodb::bson::de::Error> for Error {
    fn from(err: mongodb::bson::de::Error) -> Self {
        Self::BsonDeser(err)
    }
}
impl From<mongodb::bson::extjson::de::Error> for Error {
    fn from(err: mongodb::bson::extjson::de::Error) -> Self {
        Self::BsonExtjson(err)
    }
}
impl From<mongodb::bson::oid::Error> for Error {
    fn from(err: mongodb::bson::oid::Error) -> Self {
        Self::Oid(err)
    }
}

impl std::error::Error for Error {}

use common_uu::JsonVExentd;
use mongodb::bson;
pub use mongodb::bson::Document;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::str::FromStr;

// #[async_trait::async_trait]
// pub trait MongodbCollection {
//     type T: Serialize + DeserializeOwned + Unpin + Debug + Send + Sync;
//     async fn collection() -> &'async_trait mongodb::Collection<Self::T>;
// }

#[async_trait::async_trait]
pub trait MongodbSelect: Serialize + DeserializeOwned + Unpin + Debug + Send + Sync {
    async fn collection() -> &'async_trait mongodb::Collection<Document>;

    async fn select_one(
        where_: serde_json::Map<String, serde_json::Value>,
    ) -> Result<Option<Self>, Error> {
        use std::convert::TryInto;
        let mut where_: mongodb::bson::Document = where_
            .try_into()
            .map_err(|e: mongodb::bson::extjson::de::Error| Error::Wrapper(e.to_string()))?;

        if let Some(..) = where_.remove("$limit") {
            Err(Error::Wrapper(
                "select_one cannot input parameters: $limit".to_string(),
            ))?;
        }
        if let Some(..) = where_.remove("$sort") {
            Err(Error::Wrapper(
                "select_one cannot input parameters: $sort".to_string(),
            ))?;
        }

        debug!(
            "db_collection: {}; select_one, where: {}",
            Self::collection().await.name(),
            json!(where_).to_string()
        );

        if let Some(bson::Bson::String(id)) = where_.remove("_id") {
            let query = mongodb::bson::oid::ObjectId::from_str(&id).map_err(|e| {
                Error::Wrapper(format!(
                    "'_id' cannot be is: [{}] when select_one; error: {:?}",
                    id, e,
                ))
            })?;
            let _ = where_.insert("_id", query);
        }

        let r = Self::collection().await.find_one(where_, None).await?;
        let r = match r{
            None => None,
            Some(v) => { 
                let r: Result<Self, _> = mongodb::bson::from_document(v);
                match r{
                    Ok(v) => Some(v),
                    Err(e) => {
                        debug!( "db_collection: {}; select_one error: {:?}", Self::collection().await.name(), e);
                        None
                    }
                }
            }
        };
        
        Ok(r)
    }
    async fn select_list(
        where_: serde_json::Map<String, serde_json::Value>,
    ) -> Result<Vec<Self>, Error> {
        use futures::TryStreamExt;
        use std::convert::TryInto;

        let mut where_: Document = where_
            .try_into()
            .map_err(|e: mongodb::bson::extjson::de::Error| Error::Wrapper(e.to_string()))?;

        let mut option = mongodb::options::FindOptions::default();
        if let Some(mongodb::bson::Bson::Document(v)) = where_.remove("$sort") {
            option.sort = Some(v);
        }
        if let Some(mongodb::bson::Bson::Int64(v)) = where_.remove("$limit") {
            option.limit = Some(v);
        }

        debug!(
            "db_collection: {}; select_list, where: {}; option: {}",
            Self::collection().await.name(),
            json!(where_).to_string(),
            json!(option).to_string()
        );
        
        /*let mut cursor = Self::collection()
            .await
            .find(where_, option)
            .await?;

        let mut list = vec![];
        loop {
            let r = cursor.try_next().await;
            match r {
                Ok(None) => break,
                Ok(Some(v)) => list.push(v),
                Err(e) => {
                    warn!( "db_collection: {}; select_list, try_next() error: {:?}", Self::collection().await.name(), e);
                }
            }
        }*/

        let list: Vec<Document> = Self::collection()
            .await
            .find(where_, option)
            .await?
            .try_collect()
            .await?;

        let mut r_arr = vec![];
        for x in list {
            let r: Result<Self, _>  = mongodb::bson::from_document(x);
            match r {
                Ok(v) => r_arr.push(v),
                Err(e) => {
                    debug!( "db_collection: {}; select_one error: {:?}", Self::collection().await.name(), e);
                }
            }
        }

        Ok(r_arr)
    }

    async fn aggregate(where_: serde_json::Value) -> Result<Vec<Self>, Error> {
        use futures::TryStreamExt;

        let mut query: Vec<Document> = vec![];
        for v in where_
            .as_arrary2()
            .ok_or_else(|| Error::Wrapper("aggregate params is not arrary".to_owned()))?
        {
            query.push(bson::to_document(&v)?);
        }

        debug!(
            "db_collection: {}; aggregate(), where: {}",
            Self::collection().await.name(),
            json!(query).to_string()
        );

        let list: Vec<Document> = Self::collection()
            .await
            .aggregate(query, None)
            .await?
            .try_collect()
            .await?;

        let mut r = vec![];
        for x in list {
            match mongodb::bson::from_document::<Self>(x) {
                Err(e) => debug!("from_document: {:?}", e),
                Ok(v) => r.push(v),
            }
        }
        Ok(r)
    }
}

#[async_trait::async_trait]
pub trait MongodbUpdate: Serialize + DeserializeOwned + Unpin + Debug + Send + Sync {
    async fn collection() -> &'async_trait mongodb::Collection<Document>;

    async fn insert(&self) -> Result<String, Error> {
        let mut json_val = bson::to_document(self)?;
        // let mut json_val = json!(self);
        let _ = json_val.remove("_id");

        debug!(
            "db_collection: {}; insert(), bean: {}",
            Self::collection().await.name(),
            json!(json_val).to_string()
        );

        let r = Self::collection().await.insert_one(json_val, None).await?;
        let id = r
            .inserted_id
            .as_object_id()
            .map(|v| v.to_string())
            .unwrap_or_default();
        Ok(id)
    }
    async fn update(&self) -> Result<(), Error> {
        let mut u = mongodb::bson::to_document(self)?;
        let id = match u.remove("_id") {
            Some(mongodb::bson::Bson::String(v)) => v,
            _ => Err(Error::Wrapper(
                "'_id' cannot be empty when updating".to_string(),
            ))?,
        };

        if u.is_empty() {
            error!(
                "db_collection: {}; update(), params is null; return directly;",
                Self::collection().await.name(),
            );
            return Ok(());
        }

        // 按需修改字段
        let u = bson::doc! {"$set": u};

        let query = mongodb::bson::oid::ObjectId::from_str(&id).map_err(|e| {
            Error::Wrapper(format!(
                "'_id' cannot be is: [{}] when updating; error: {:?}",
                id, e
            ))
        })?;
        let query = bson::doc! {"_id": query};

        let log_query = json!(query).to_string();
        let log_bean = json!(u).to_string();
        debug!(
            "db_collection: {}; update(), query: {}, bean: {}",
            Self::collection().await.name(),
            log_query,
            log_bean,
        );

        let r = Self::collection().await.update_one(query, u, None).await?;
        if r.matched_count == 0 {
            Err(Error::Wrapper(format!(
                "the modified document does not match; query: {}, bean: {}",
                log_query, log_bean
            )))?;
        }

        Ok(())
    }
}
